如何理解 RPC 远程服务调用?
0. 引言
微服务架构下,服务之间互相调用——调用方希望"像调用本地方法一样调用远程服务",这就是 RPC(Remote Procedure Call,远程过程调用)。RPC 的难点在于:屏蔽网络差异(序列化、传输、编解码)、自动定位服务(注册中心)、处理故障(超时、重试、熔断)。本文拆解 RPC 的四要素与一条完整调用链。
1. RPC 要解决的核心问题
图表渲染中…
| 要素 | 作用 | 典型实现 |
|---|---|---|
| 动态代理 | 让调用方无感:接口 → 代理对象 → 网络调用 | JDK Proxy、Javassist、ByteBuddy |
| 序列化协议 | 对象与字节流的互转,决定性能与兼容性 | JSON、Hessian2、Protobuf、Kryo |
| 网络传输 | 连接管理与 IO 模型 | Netty(TCP)、HTTP/2(gRPC)、QUIC |
| 服务发现/路由 | 找到目标实例、负载均衡 | 注册中心 + 负载均衡策略 |
2. 一条 Dubbo 调用的完整链路
图表渲染中…
- 接口契约:Provider 与 Consumer 共享接口 jar(或同构接口),保证参数/返回值一致;
- 协议:Dubbo 3.x 默认 Triple 协议(tri://,基于 HTTP/2 + Protobuf),兼容 gRPC;旧版默认 Dubbo 协议(TCP);
- 集群容错:Failover(默认,重试其他节点)、Failfast、Failsafe、Failback、Forking;
- 超时与重试:
timeout+retries,注意重试要幂等(写操作重试可能重复下单)。
3. RPC vs HTTP 接口
| 维度 | RPC(Dubbo/gRPC) | HTTP REST |
|---|---|---|
| 性能 | 高(二进制协议、长连接、连接复用) | 中(文本协议、短连接开销) |
| 协议 | Dubbo/Triple/gRPC 二进制 | HTTP/1.1、HTTP/2、JSON |
| 服务治理 | 内置(注册发现、负载均衡、熔断、限流) | 需配套网关/注册中心 |
| 跨语言 | gRPC 好(Protobuf),Dubbo 以 Java 为主 | 天然跨语言 |
| 适用 | 服务间内部调用(BFF 以下) | 对外 API、异构系统集成、浏览器 |
实践共识:内部服务用 RPC(性能 + 治理),外部接口用 HTTP(兼容 + 规范)。gRPC 是"跨语言 + 高性能"的折中,已成为云原生 RPC 事实标准(K8s 生态、Google API)。
4. 序列化选型要点
| 协议 | 性能 | 体积 | 跨语言 | 场景 |
|---|---|---|---|---|
| JSON | 低 | 大 | ✅ | 调试方便、对外接口 |
| Hessian2 | 中 | 中 | 部分 | Dubbo 传统默认 |
| Protobuf | 高 | 小 | ✅ | gRPC、高性能内部调用 |
| Kryo | 高 | 小 | ❌ | Java 内部高性能场景 |
序列化安全提示:反序列化是不可信输入——避免使用 Java 原生序列化(
ObjectInputStream)处理外部数据,历史上有大量反序列化 RCE 漏洞。
5. 面试高频问题
- RPC 和 HTTP 的区别? 见上表——本质差异是"二进制协议 + 长连接 + 服务治理内置" vs "文本协议 + 通用";
- 为什么 RPC 需要注册中心? 实例动态上下线,调用方需要实时可用的实例列表做负载均衡与故障转移;
- RPC 超时怎么处理? 超时 + 重试(幂等才重试)+ 熔断降级 + 异步化;
- 如何保证 RPC 安全? 内网隔离 + 认证(token/mTLS)+ 限流 + 参数校验。
6. 小结
- RPC 四要素:动态代理、序列化、网络传输、服务发现;
- Dubbo 调用链:接口 → 代理 → 序列化 → Netty → 反序列化 → 执行 → 返回;
- 内部服务用 RPC(Dubbo/gRPC),对外用 HTTP REST;
- 超时重试必须幂等,序列化要防注入。
下一章讲解为什么微服务需要 API 网关:路由、鉴权、限流与灰度发布。